package quorum;
import common.*;
import java.util.Set;
import java.util.HashSet;
import utils.Hashes;

/***
 *   Msg forms: QUORUMTASK!action!targer!value
 *   			IAMALIVE
 *   			IAMALIVELEADER
 *   			PUSH!target entity!whole list
 */

import broadcast.BroadcastListener;
public class QuorumListener implements BroadcastListener{
private Quorum parent;
private static boolean DEBUG=false;
	public QuorumListener(Quorum p){
		parent = p;
	}
	public void onDelivery(Message m) {
		String s=new String(m.getData());
		Entity source=m.getSource();
		/*if(source.getId()>3 || source.getId()<0){
			System.out.println("suck");
		}*/
		if(DEBUG){
		System.out.println("  **MESSAGE** "+source+" : "+s);
		}
		String[] as=s.split("!");
		
		/* QUORUMTASK!action!target!id ip (id password)*/
		if((as[0].equals("QUORUMTASK"))){
		/* as[0]:QUORUMTASK as[1]:action as[2]:target as[3]:value*/
		/* es[0]:id     es[1]:ip(/password if static client)*/
		String[] es=as[3].split(" ");
		switch(Integer.valueOf(as[1]).intValue()){
		case QuorumTask.ACTION_INSERT:
			/*ACTION_INSERT*/
			insert(Integer.valueOf(as[2]).intValue(),es);
			//server
			if(parent.isLeader()&& parent.getId()!=source.getId() && Integer.valueOf(as[2]).intValue()==QuorumTask.TARGET_LIVE_SERVERS){
				if(source.getId()!=parent.getId()){
					Entity toadd=new Entity(Integer.valueOf(es[0]).intValue(),es[1]);
					QuorumTask task=new QuorumTask(QuorumTask.ACTION_INSERT,Integer.valueOf(as[2]).intValue(),toadd.toString());
					try{
						parent.writeChildren(task);
					}catch(Exception e){}
				}
			}
			//client
			if(Integer.valueOf(as[2]).intValue()==QuorumTask.TARGET_LIVE_CLIENTS){
				int sourcegroup=Hashes.getGroupIdFromServerId(source.getId());
				int parentgroup=Hashes.getGroupIdFromServerId(parent.getId());
//				if(sourcegroup==parentgroup && parent.isLeader()){
//					QuorumTask task=new QuorumTask(QuorumTask.ACTION_INSERT,QuorumTask.TARGET_LIVE_CLIENTS,es[0]+" "+es[1]);
//					parent.writeSibling(task);					
//				}
				if(sourcegroup!=parentgroup && parent.isLeader()){
					QuorumTask task=new QuorumTask(QuorumTask.ACTION_INSERT,QuorumTask.TARGET_LIVE_CLIENTS,es[0]+" "+es[1]);
					parent.writeChildren(task);
				}
			}
			break;
		case QuorumTask.ACTION_DELETE:
			/*ACTION_DELETE*/
			if(DEBUG){
			System.out.println(" --"+as[2]+"  "+es[0]+":"+es[1]);
			}
			delete(Integer.valueOf(as[2]).intValue(),es);
			//server
			if(parent.isLeader()&& parent.getId()!=source.getId() && Integer.valueOf(as[2]).intValue()==QuorumTask.TARGET_LIVE_SERVERS){
				if(source.getId()!=parent.getId()){
					Entity toadd=new Entity(Integer.valueOf(es[0]).intValue(),es[1]);
					QuorumTask task=new QuorumTask(QuorumTask.ACTION_DELETE,Integer.valueOf(as[2]).intValue(),toadd.toString());
					try{
						parent.writeChildren(task);
					}catch(Exception e){}
				}
			}
			//clients
			if(Integer.valueOf(as[2]).intValue()==QuorumTask.TARGET_LIVE_CLIENTS){
				int sourcegroup=Hashes.getGroupIdFromServerId(source.getId());
				int parentgroup=Hashes.getGroupIdFromServerId(parent.getId());
//				if(sourcegroup==parentgroup && parent.isLeader()){
//					QuorumTask task=new QuorumTask(QuorumTask.ACTION_INSERT,QuorumTask.TARGET_LIVE_CLIENTS,es[0]+" "+es[1]);
//					parent.writeSibling(task);					
//				}
				if(sourcegroup!=parentgroup && parent.isLeader()){
					QuorumTask task=new QuorumTask(QuorumTask.ACTION_DELETE,QuorumTask.TARGET_LIVE_CLIENTS,es[0]+" "+es[1]);
					parent.writeChildren(task);
				}
			}
			break;
//		case QuorumTask.ACTION_UPDATE:
//			/*ACTION_UPDATE*/
//			update(Integer.valueOf(as[2]).intValue(),es);
//			break;
		default:
			break;
		}
		}
		if((as[0].equals("NEWBORN"))&&parent.getId()!=source.getId()&& parent.isLeader()){
			String liveStr="PUSH!"+source.getId()+"!"+Lists.getInstance().liveString();
			Message msg=new Message(liveStr.getBytes());
			msg.setSource(parent);
			parent.getGroupBroadcast().broadcast(msg);
		}
		if((as[0].equals("IAMALIVE"))&&parent.getId()!=source.getId()){
//			not dead
			if(checkLive.getInstance().isListening(source)){
				checkLive.getInstance().interrupt(source);
			}else{
//			new join
				if(parent.isLeader()){
					String liveStr="PUSH!"+source.getId()+"!"+Lists.getInstance().liveString();
					
					Message msg=new Message(liveStr.getBytes());
					msg.setSource(parent);
					parent.getGroupBroadcast().broadcast(msg);
					
					QuorumTask task=new QuorumTask(QuorumTask.ACTION_INSERT,QuorumTask.TARGET_LIVE_SERVERS,source.toString());
					try{
						parent.writeChildren(task);
						parent.writeSibling(task);
					}catch(Exception e){}
				}
			}		
		}
		
		
		if((as[0].equals("IAMALIVELEADER"))){
			if(parent.isLeader() && parent.getId()!=source.getId()){
				
				int group=Hashes.getGroupIdFromServerId(source.getId());
				if(Hashes.getGroupIdFromServerId(source.getId())!=Hashes.getGroupIdFromServerId(parent.getId())){
					//real leader - normal case
					if(checkLive.getInstance().isListening(source)){
						checkLive.getInstance().interrupt(source);
					}else{
						/*Entity e=Lists.getInstance().getLiveServerGroup(group).peek();
						if(source.getId()==e.getId() && checkLive.getInstance().isListening(e)){*/
						if(checkLive.getInstance().isListeningLeader(group)){
							/**newly alive && is already listening the group - ignore*/
						//is listening the real server, do nothing
						}else{
//						System.out.println("xxx"+group+"xxx");
						if(Lists.getInstance().getLiveServerGroup(group).size()==0){
							/**originally the whole group is dead - PUSH !!!*/
							String c[]=new String[2];
							c[0]=(new Integer(source.getId())).toString();
							c[1]=source.getIp();
							insert(QuorumTask.TARGET_LIVE_SERVERS,c);
//							!!!!!!!!!!!!PUSH
							/**get some order and just one leader is responsible to PUSH*/
							boolean responsible = true;
							int mygroup=Hashes.getGroupIdFromServerId(parent.getId());
							int tt=mygroup;
							if(mygroup<group){
								tt=mygroup+Lists.GROUP_SIZE;
							}
							for(int w=group+1;w<tt;w++){
								if(w>=Lists.GROUP_SIZE)
									w=w-Lists.GROUP_SIZE;
								if(Lists.getInstance().getLiveServerGroup(w).size()>0){
									responsible=false;
									break;
								}
							}
							if(responsible){
								String liveStr="PUSH!"+c[0]+"!"+Lists.getInstance().liveString();							
								Message msg=new Message(liveStr.getBytes());
								msg.setSource(parent);
								parent.getSiblingBroadcast().setDestinations(parent.getSiblingDest());
								parent.getSiblingBroadcast().broadcast(msg);
							}
							checkLive.getInstance().addListenEntity(source);
							QuorumTask task=new QuorumTask(QuorumTask.ACTION_INSERT,QuorumTask.TARGET_LIVE_SERVERS,source.toString());
							parent.writeChildren(task);
						}
						
					}
					}
				}
			}
		}
		
		if((as[0].equals("PUSH")) && Integer.valueOf(as[1]).intValue()==parent.getId()){
			//if(parent.getId()!=source.getId()){
				Lists.getInstance().constructLive(as[2]);
			//}
		}
		
		if(as[0].equals("SYNCSERVER")){
			/*SYNCSERVER!groupId@id ip@...@id ip*/
			int group=Hashes.getGroupIdFromServerId(source.getId());
			int parentgroup=Hashes.getGroupIdFromServerId(parent.getId());
			if(group==parentgroup){
			Lists.getInstance().syncLiveGroup(as[1]);
			}

			if(parent.isLeader() && group!=parentgroup){
			//	Lists.getInstance().syncLiveGroup(as[1]);
				Message msg=new Message(m.getData());
				msg.setSource(parent);
				Quorum.getInstance().getGroupBroadcast().broadcast(msg);
			}
		}
	}
	
	public boolean insert(int i,String[] content){
		if(DEBUG){
		System.out.println(" INSERT ### "+content[0]+" "+content[1]+" ###");
		}
		Lists lists=Lists.getInstance();
		checkLive checklive=checkLive.getInstance();
		switch(i){
		case QuorumTask.TARGET_LIVE_SERVERS:
			/*live servers*/
			Entity toadd=new Entity(Integer.valueOf(content[0]).intValue(),content[1]);
			int group=Hashes.getGroupIdFromServerId(toadd.getId());
			lists.addLiveServer(toadd);
			//if in the same group, also add listener
			if(Hashes.getGroupIdFromServerId(parent.getId())==group){
			checklive.addListenEntity(toadd);
			}else{
				if(parent.isLeader()){
			//if not same group but leader
					if(Lists.getInstance().getLiveServerGroup(group).peek().getId()==toadd.getId()){
						checklive.removeListenLeader(group);
						checklive.addListenEntity(toadd);
					}
				}
			}
			return true;
		case QuorumTask.TARGET_LIVE_CLIENTS:
			/*live client*/
			return lists.addLiveClient(new ClientEntity(Integer.valueOf(content[0]).intValue(),content[1]));
//		case 2:
//			/*static server*/
////			return lists.addStaticServer(new Entity(Integer.valueOf(content[0]).intValue(),content[1]));
//			return false;
//		case 3:
//			/*static clients*/
////			return lists.addStaticClient(new ClientEntity(Integer.valueOf(content[0]).intValue(),content[1]));
//			return false;
		default:
			return false;
		}
	}
	public boolean delete(int i,String[] content){
		Lists lists=Lists.getInstance();
		checkLive checklive=checkLive.getInstance();
		switch(i){
		case QuorumTask.TARGET_LIVE_SERVERS:
			/*live servers*/
			Entity todelete=new Entity(Integer.valueOf(content[0]).intValue(),content[1]);
			int group=Hashes.getGroupIdFromServerId(todelete.getId());
			
			//if in same group, also remove listener
			if(Hashes.getGroupIdFromServerId(parent.getId())==group){
				checklive.removeListenEntity(todelete);
			}else{
				if(parent.isLeader()){
					if(Lists.getInstance().getLiveServerGroup(group).peek().getId()==todelete.getId()){
						checklive.removeListenLeader(group);
						lists.deleteLiveServer(todelete);
						Entity en=Lists.getInstance().getLiveServerGroup(group).peek();
						if(en!=null){
							checklive.addListenEntity(en);
						}
						return true;
					}
				}
//					if not same group but leader?
			}
			lists.deleteLiveServer(todelete);
			return true;
		case QuorumTask.TARGET_LIVE_CLIENTS:
			/*live client*/
			return lists.deleteLiveClient(new ClientEntity(Integer.valueOf(content[0]).intValue(),content[1]));
//		case 2:
//			/*static server*/
////			return lists.deleteStaticServer(new Entity(Integer.valueOf(content[0]).intValue(),content[1]));
//			return false;
//		case 3:
//			/*static clients*/
////			return lists.deleteStaticClient(new ClientEntity(Integer.valueOf(content[0]).intValue(),content[1]));
//			return false;
		default:
			return false;
		}
	}
	
	/**
	 * update is not needed?
	 * */
	public boolean update(int i,String[] content){
		switch(i){
		case QuorumTask.TARGET_LIVE_SERVERS:
			/*static servers*/
			break;
		case QuorumTask.TARGET_LIVE_CLIENTS:
			/*live servers*/
			break;
//		case 2:
//			/*static clients*/
//			break;
//		case 3:
//			/*live servers*/
//			break;
		default:
			return false;
		}
		return true;
	}
}